package org.smart4j.framework.helper;


import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart4j.framework.bean.FileParam;
import org.smart4j.framework.bean.FormParam;
import org.smart4j.framework.bean.Param;
import org.smart4j.framework.util.CollectionUtil;
import org.smart4j.framework.util.FileUtil;
import org.smart4j.framework.util.StreamUtil;
import org.smart4j.framework.util.StringUtil;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Created by liangyue on 2018/1/13.
 */
public final class UploadHelper {
	private static final Logger LOGGER = LoggerFactory.getLogger(UploadHelper.class);

	/**
	 * Apache Commons FileUpload提供的Servlet文件上传对象
	 */
	private static ServletFileUpload servletFileUpload;

	/**
	 * 初始化
	 * 在该方法中初始化prg.apache.commons.fileupload.servlet.ServletFileUpload对象，
	 * 一般情况下，只需设置一个上传文件的临时目录与上传文件的最大限制；上传文件的临时目录可设置为
	 * 应用服务器的临时目录，上传文件的最大限制可让用户自行配置，从配置文件中获取。
	 */
	public static void init(ServletContext servletContext){
		File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
		servletFileUpload = new ServletFileUpload(new DiskFileItemFactory(
				DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD, repository));
		int uploadLimit = ConfigHelper.getAppUploadLimit();
		if (uploadLimit != 0){
			servletFileUpload.setFileSizeMax(uploadLimit * 1024 * 1024);
		}
	}

	/**
	 * 判断请求是否为multipart类型
	 * 只有在上传文件是对应的请求类型才是multipart类型，也就是说，
	 * 可通过isMultipart方法来判断当前请求是否为文件上传请求。
	 * @param request
	 * @return
	 */
	public static boolean isMultipart(HttpServletRequest request){
		return ServletFileUpload.isMultipartContent(request);
	}
	//我觉得是这样的，MemStore里面是Cell是基本单元。
	//如果想存储进PFile，需求比较简单，我们只需要里面的数据就好了。但是Cell里面的东西太多了，如果直接使用这个，好像需要调用好几级才能获取到key和value。
	// 所以重构PMemStore,内存中只存储需要的rowKey和对应的那一行的值。
	//等到需要内存满了需要flush的时候，原来是从MemStore进行flush，现在变成了从PMemStore进行flush，
	// 因为内存中的PFile是存在PMemStore中的，所以叫Change Write Path.
	//由Client->MemStore->HFile变成了Client->PMemStore->PFile
	//

	/**
	 * 创建请求对象
	 * @param request
	 * @return
	 * @throws IOException
	 */
	public static Param createParam(HttpServletRequest request) throws IOException{
		List<FormParam> formParamList = new ArrayList<>();
		List<FileParam> fileParamList = new ArrayList<>();

		try {
			Map<String, List<FileItem>> fileItemListMap = servletFileUpload.parseParameterMap(request);
			if (CollectionUtil.isNotEmpty(fileItemListMap)){
				for (Map.Entry<String, List<FileItem>> fileItemListEntry : fileItemListMap.entrySet()){
					String fieldName = fileItemListEntry.getKey();
					List<FileItem> fileItemList = fileItemListEntry.getValue();
					if (CollectionUtil.isNotEmpty(fileItemList)){
						for (FileItem fileItem : fileItemList){
							if (fileItem.isFormField()){
								String fieldValue = fileItem.getString("UTF-8");
								formParamList.add(new FormParam(fieldName, fieldValue));
							}
							else {
								String fileName = FileUtil.getRealFileName(
										new String(fileItem.getName().getBytes(), "UTF-8"));
								if (StringUtil.isNotEmpty(fileName)){
									long fileSize = fileItem.getSize();
									String contentType = fileItem.getContentType();
									InputStream inputStream = fileItem.getInputStream();
									fileParamList.add(new FileParam(fieldName, fileName, fileSize, contentType, inputStream));
								}

							}
						}
					}
				}
			}
		} catch (FileUploadException e) {
			LOGGER.error("create param failure", e);
			throw new RuntimeException(e);
		}
		return new Param(formParamList, fileParamList);
	}


	/**
	 * 上传文件
	 * @param basePath
	 * @param fileParam
	 */
	public static void upLoadFile(String basePath, FileParam fileParam){
		try {
			if (fileParam != null){
				String filePath = basePath + fileParam.getFileName();
				FileUtil.createFile(filePath);
				InputStream inputStream = new BufferedInputStream(fileParam.getInputStream());
				OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(filePath));
				StreamUtil.copySteam(inputStream, outputStream);
			}
		} catch (FileNotFoundException e) {
			LOGGER.error("upload file failure", e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * 批量上传文件
	 * @param basePath
	 * @param fileParamList
	 */
	public static void uploadFile(String basePath, List<FileParam> fileParamList){
		try {
			if (CollectionUtil.isNotEmpty(fileParamList)){
				for (FileParam fileParam : fileParamList){
					upLoadFile(basePath, fileParam);
				}
			}
		} catch (Exception e) {
			LOGGER.error("upload file failure", e);
			throw new RuntimeException(e);
		}

	}
}
